package com.example.dbdoc.service;

import com.example.dbdoc.model.ColumnInfo;
import com.example.dbdoc.model.IndexInfo;
import com.example.dbdoc.model.TableInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.example.dbdoc.exception.DatabaseDocException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 文档生成服务
 * 
 * @author 系统生成
 * @version 1.0.0
 */
@Service
public class DocumentGeneratorService {

    private static final Logger logger = LoggerFactory.getLogger(DocumentGeneratorService.class);

    @Value("${documentation.template-version:1.0.0}")
    private String templateVersion;

    @Value("${documentation.author:系统生成}")
    private String author;

    @Value("${documentation.database-name:数据库}")
    private String databaseName;

    @Value("${documentation.output-path:./output/}")
    private String outputPath;

    /**
     * 获取文件名称
     */
    public String getFileName(){
        return getTitle() + "_" + templateVersion + ".md";
    }

    /**
     * 获取标题
     */
    public String getTitle(){
        return databaseName + "数据库设计说明书";
    }

    /**
     * 生成完整的markdown文档
     */
    public String generateMarkdown(List<TableInfo> tables) {
        logger.info("开始生成markdown文档，表数量: {}", tables.size());

        StringBuilder markdown = new StringBuilder();

        // 生成文档标题
        markdown.append("# ").append(getTitle()).append("\n\n");

        // 生成修改历史记录
        markdown.append(generateChangeHistory());

        // 生成数据结构章节
        markdown.append(generateDataStructure(tables));

        logger.info("markdown文档生成完成，总长度: {} 字符", markdown.length());
        return markdown.toString();
    }

    /**
     * 生成修改历史记录表格
     */
    private String generateChangeHistory() {
        logger.debug("生成修改历史记录表格...");

        StringBuilder sb = new StringBuilder();
        sb.append("## 1.修改历史记录\n\n");

        // 表格头部
        sb.append("| 版本  | 日期      | amd  | 修订者 | 说明     |\n");
        sb.append("| ----- | --------- | ---- | ------ | -------- |\n");

        // 当前版本记录
        String currentDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-M-d"));
        sb.append("| ").append(templateVersion).append(" | ")
                .append(currentDate).append(" | a    | ")
                .append(author).append(" | 自动生成 |\n\n");

        sb.append("\n\n\n");

        return sb.toString();
    }

    /**
     * 生成数据结构章节
     */
    private String generateDataStructure(List<TableInfo> tables) {
        logger.debug("生成数据结构章节...");

        StringBuilder sb = new StringBuilder();
        sb.append("## 2.数据结构\n");
        
        // 按表名前缀分组
        Map<String, List<TableInfo>> moduleGroups = groupTablesByPrefix(tables);
        
        int moduleIndex = 1;
        for (Map.Entry<String, List<TableInfo>> entry : moduleGroups.entrySet()) {
            String prefix = entry.getKey();
            List<TableInfo> moduleTables = entry.getValue();
            String moduleName = getModuleName(prefix);
            
            sb.append("### 2.").append(moduleIndex).append(".").append(moduleName).append("\n\n");
            
            for (int i = 0; i < moduleTables.size(); i++) {
                TableInfo table = moduleTables.get(i);
                logger.debug("生成表 {} 的文档...", table.getTableName());
                sb.append(generateTableDocumentation(table));
                
                // 在表与表之间添加空行（除了最后一个表）
                if (i < moduleTables.size() - 1) {
                    sb.append("\n");
                }
            }
            
            // 在模块与模块之间添加空行（除了最后一个模块）
            if (moduleIndex < moduleGroups.size()) {
                sb.append("\n");
            }
            
            moduleIndex++;
        }

        return sb.toString();
    }
    
    /**
     * 按表名前缀分组
     */
    private Map<String, List<TableInfo>> groupTablesByPrefix(List<TableInfo> tables) {
        Map<String, List<TableInfo>> groups = new LinkedHashMap<>();
        
        for (TableInfo table : tables) {
            String prefix = extractTablePrefix(table.getTableName());
            groups.computeIfAbsent(prefix, k -> new ArrayList<>()).add(table);
        }
        
        return groups;
    }
    
    /**
     * 提取表名前缀
     */
    private String extractTablePrefix(String tableName) {
        if (tableName.contains("_")) {
            return tableName.substring(0, tableName.indexOf("_"));
        }
        return "other"; // 没有前缀的表归为其他
    }
    
    /**
     * 根据前缀获取模块名称
     */
    private String getModuleName(String prefix) {
        Map<String, String> prefixToModule = new HashMap<>();
        prefixToModule.put("act", "活动表结构");
        prefixToModule.put("user", "用户表结构");
        prefixToModule.put("order", "订单表结构");
        prefixToModule.put("product", "商品表结构");
        prefixToModule.put("sys", "系统表结构");
        prefixToModule.put("log", "日志表结构");
        prefixToModule.put("config", "配置表结构");
        prefixToModule.put("other", "其他表结构");
        
        return prefixToModule.getOrDefault(prefix, prefix + "表结构");
    }

    /**
     * 生成单个表的文档
     */
    private String generateTableDocumentation(TableInfo table) {
        StringBuilder sb = new StringBuilder();

        // 表标题 - 格式：中文名(表名)
        String tableTitle;
        if (table.getTableComment() != null && !table.getTableComment().trim().isEmpty()) {
            tableTitle = table.getTableComment() + "(" + table.getTableName() + ")";
        } else {
            tableTitle = table.getTableName();
        }
        sb.append("#### ").append(tableTitle).append("\n\n");

        // 生成索引信息表格
        sb.append(generateIndexTable(table.getIndexes(), table.getColumns()));

        // 生成字段信息表格
        sb.append(generateColumnTable(table.getColumns(), table.getIndexes()));

        return sb.toString();
    }

    /**
     * 生成索引信息表格
     */
    private String generateIndexTable(List<IndexInfo> indexes, List<ColumnInfo> columns) {
        if (indexes.isEmpty()) {
            return "";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("\n");

        // 表格头部
        sb.append("|   类型    | 名称        | 序号 | 字段名       | 备注         |\n");
        sb.append("| :-------: | ----------- | ---- | ------------ | ------------ |\n");

        // 创建字段名到注释的映射
        Map<String, String> columnCommentMap = new HashMap<>();
        for (ColumnInfo column : columns) {
            String comment = (column.getComment() != null && !column.getComment().trim().isEmpty()) 
                           ? column.getComment() : "";
            // 转义Markdown表格特殊字符
            comment = escapeMarkdownTableCell(comment);
            columnCommentMap.put(column.getColumnName(), comment);
        }
        
        // 收集主键字段
        Set<String> primaryKeyColumns = new HashSet<>();
        for (IndexInfo index : indexes) {
            if ("PRIMARY".equals(index.getDisplayName())) {
                primaryKeyColumns.addAll(index.getColumns());
            }
        }
        
        // 过滤索引：如果字段已经是主键，就不显示它的唯一索引
        List<IndexInfo> filteredIndexes = new ArrayList<>();
        for (IndexInfo index : indexes) {
            // 主键始终保留
            if ("PRIMARY".equals(index.getDisplayName())) {
                filteredIndexes.add(index);
                continue;
            }
            
            // 如果是唯一索引，且所有字段都在主键中，则跳过
            if ("UNIQUE".equals(index.getIndexType().toUpperCase())) {
                boolean allColumnsInPrimaryKey = true;
                for (String columnName : index.getColumns()) {
                    if (!primaryKeyColumns.contains(columnName)) {
                        allColumnsInPrimaryKey = false;
                        break;
                    }
                }
                if (allColumnsInPrimaryKey && index.getColumns().size() == 1) {
                    // 单字段唯一索引且该字段是主键，跳过
                    continue;
                }
            }
            
            filteredIndexes.add(index);
        }

        // 按索引类型排序：主键 > 唯一索引 > 普通索引
        filteredIndexes.sort((a, b) -> {
            int priorityA = getIndexPriority(a.getIndexType());
            int priorityB = getIndexPriority(b.getIndexType());
            return Integer.compare(priorityA, priorityB);
        });

        // 为索引分配序号
        Map<String, String> indexSuffixMap = generateIndexSuffixes(filteredIndexes);

        for (IndexInfo index : filteredIndexes) {
            String indexType = index.getIndexTypeDescription();
            String indexName = index.getDisplayName();
            String indexSuffix = indexSuffixMap.get(indexName);
            
            // 如果不是主键，在类型后面加上序号
            String displayType = indexType;
            if (!"PRIMARY".equals(indexName) && indexSuffix != null) {
                displayType = indexType + indexSuffix;
            }

            // 为每个索引的每个字段生成一行
            List<String> indexColumns = index.getColumns();
            for (int i = 0; i < indexColumns.size(); i++) {
                String columnName = indexColumns.get(i);
                int sequence = i + 1;
                String columnComment = columnCommentMap.getOrDefault(columnName, "");

                if (i == 0) {
                    // 第一行显示索引类型和名称
                    sb.append("| ").append(displayType).append(" | ")
                            .append(indexName).append(" | ")
                            .append(sequence).append(" | ")
                            .append(columnName).append(" | ")
                            .append(columnComment).append(" |\n");
                } else {
                    // 后续行只显示序号和字段名
                    sb.append("| | | ")
                            .append(sequence).append(" | ")
                            .append(columnName).append(" | ")
                            .append(columnComment).append(" |\n");
                }
            }
        }

        sb.append("\n\n\n");
        return sb.toString();
    }
    
    /**
     * 为索引生成后缀编号
     */
    private Map<String, String> generateIndexSuffixes(List<IndexInfo> indexes) {
        Map<String, String> suffixMap = new HashMap<>();
        int uniqueCounter = 1;
        int normalCounter = 1;
        
        for (IndexInfo index : indexes) {
            String indexType = index.getIndexType().toUpperCase();
            String indexName = index.getDisplayName();
            
            if ("PRIMARY".equals(indexName)) {
                continue; // 主键不需要后缀
            }
            
            if ("UNIQUE".equals(indexType)) {
                suffixMap.put(indexName, "UI" + uniqueCounter);
                uniqueCounter++;
            } else {
                suffixMap.put(indexName, "ID" + normalCounter);
                normalCounter++;
            }
        }
        
        return suffixMap;
    }

    /**
     * 获取索引优先级（用于排序）
     */
    private int getIndexPriority(String indexType) {
        switch (indexType.toUpperCase()) {
            case "PRIMARY":
                return 1;
            case "UNIQUE":
                return 2;
            default:
                return 3;
        }
    }

    /**
     * 生成字段信息表格
     */
    private String generateColumnTable(List<ColumnInfo> columns, List<IndexInfo> indexes) {
        StringBuilder sb = new StringBuilder();
        sb.append("\n");

        // 表格头部
        sb.append("| 字段名       | 名称         | 类型         | 允许空 | 默认值 | 说明                                  |\n");
        sb.append("| ------------ | ------------ | ------------ | ------ | ------ | ------------------------------------- |\n");

        // 创建字段到索引的映射
        Map<String, String> columnToIndexMap = createColumnToIndexMap(indexes);

        for (ColumnInfo column : columns) {
            sb.append("| ").append(column.getColumnName()).append(" | ");

            // 名称（使用注释，如果没有注释则使用字段名）
            String displayName = (column.getComment() != null && !column.getComment().trim().isEmpty())
                    ? column.getComment() : column.getColumnName();
            // 转义Markdown表格特殊字符
            displayName = escapeMarkdownTableCell(displayName);
            sb.append(displayName).append(" | ");

            // 类型 - 格式化数据类型
            sb.append(formatDataType(column)).append(" | ");

            // 允许空
            sb.append(column.isNullable() ? "是" : "否").append(" | ");

            // 默认值
            String defaultValue = column.getDefaultValue();
            if (defaultValue == null || defaultValue.trim().isEmpty()) {
                defaultValue = "";
            }
            sb.append(defaultValue).append(" | ");

            // 说明
            List<String> descriptions = new ArrayList<>();
            
            // 如果有序列名称，显示序列名称（PostgreSQL）
            if (column.getSequenceName() != null && !column.getSequenceName().isEmpty()) {
                descriptions.add(column.getSequenceName());
            } else {
                // 否则显示 AUTO_INCREMENT（MySQL）
                if (column.isAutoIncrement()) {
                    descriptions.add("AUTO_INCREMENT");
                }
                
                // 添加索引信息（只有非序列字段才显示索引标记）
                String indexInfo = columnToIndexMap.get(column.getColumnName());
                if (indexInfo != null) {
                    descriptions.add(indexInfo);
                }
            }
            
            sb.append(String.join(" ", descriptions)).append(" |\n");
        }

        sb.append("\n\n");
        return sb.toString();
    }
    
    /**
     * 转义Markdown表格单元格中的特殊字符
     */
    private String escapeMarkdownTableCell(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        
        // 替换竖线为HTML实体，避免破坏表格结构
        text = text.replace("|", "&#124;");
        
        // 将换行符替换为空格或<br>，避免破坏表格
        text = text.replace("\n", " ");
        text = text.replace("\r", "");
        
        return text;
    }
    
    /**
     * 创建字段到索引的映射
     */
    private Map<String, String> createColumnToIndexMap(List<IndexInfo> indexes) {
        Map<String, String> columnToIndexMap = new HashMap<>();
        Map<String, String> indexSuffixMap = generateIndexSuffixes(indexes);
        
        for (IndexInfo index : indexes) {
            String indexName = index.getDisplayName();
            String indexType = index.getIndexType().toUpperCase();
            
            for (String columnName : index.getColumns()) {
                if ("PRIMARY".equals(indexName)) {
                    // 主键不显示后缀
                    continue;
                } else {
                    String suffix = indexSuffixMap.get(indexName);
                    if (suffix != null) {
                        columnToIndexMap.put(columnName, suffix);
                    }
                }
            }
        }
        
        return columnToIndexMap;
    }
    
    /**
     * 格式化数据类型
     */
    private String formatDataType(ColumnInfo column) {
        String dataType = column.getDataType().toLowerCase();
        Integer columnSize = column.getColumnSize();
        Integer decimalDigits = column.getDecimalDigits();
        
        // 特殊处理的数据类型
        switch (dataType) {
            case "text":
            case "longtext":
            case "mediumtext":
            case "tinytext":
                // TEXT类型不显示长度，显示为小写
                return dataType;
                
            case "date":
            case "datetime":
            case "timestamp":
            case "time":
                // 日期时间类型不显示长度
                return dataType;
                
            case "int":
            case "integer":
                // INT类型显示标准长度
                return "int(11)";
                
            case "bigint":
                // BIGINT类型显示标准长度
                return "bigint(20)";
                
            case "tinyint":
                // TINYINT类型显示标准长度
                return "tinyint(4)";
                
            case "smallint":
                // SMALLINT类型显示标准长度
                return "smallint(6)";
                
            case "mediumint":
                // MEDIUMINT类型显示标准长度
                return "mediumint(9)";
                
            default:
                // 其他类型按原逻辑处理，保持小写
                StringBuilder sb = new StringBuilder(dataType);
                
                if (columnSize != null && columnSize > 0) {
                    sb.append("(").append(columnSize);
                    if (decimalDigits != null && decimalDigits > 0) {
                        sb.append(",").append(decimalDigits);
                    }
                    sb.append(")");
                }
                
                return sb.toString();
        }
    }

    /*
     *
     * 保存markdown内容到文件
     */
    public String saveToFile(String content, String fileName) {
        logger.info("开始保存文档到文件: {}", fileName);

        try {
            // 确保输出目录存在
            Path outputDir = Paths.get(outputPath);
            if (!Files.exists(outputDir)) {
                Files.createDirectories(outputDir);
                logger.info("创建输出目录: {}", outputDir.toAbsolutePath());
            }

            // 构建完整的文件路径
            Path filePath = outputDir.resolve(fileName);

            // 写入文件，使用UTF-8编码
            try (FileWriter writer = new FileWriter(filePath.toFile())) {
                writer.write(content);
                writer.flush();
            }

            String absolutePath = filePath.toAbsolutePath().toString();
            logger.info("文档保存成功: {}", absolutePath);
            logger.info("文件大小: {} 字节", content.getBytes(StandardCharsets.UTF_8).length);

            return absolutePath;

        } catch (IOException e) {
            logger.error("保存文档到文件时发生IO错误: {}", e.getMessage(), e);
            logger.error("目标路径: {}", outputPath);
            logger.error("文件名: {}", fileName);

            // 尝试备用路径
            String backupPath = tryBackupPath(content, fileName);
            if (backupPath != null) {
                logger.warn("已使用备用路径保存文件");
                return backupPath;
            }

            logger.error("所有保存路径都失败，可能的原因:");
            logger.error("1. 磁盘空间不足");
            logger.error("2. 文件路径权限不足");
            logger.error("3. 文件名包含非法字符");
            logger.error("4. 目标目录不存在且无法创建");

            throw new DatabaseDocException.FileOperationException(
                    outputPath + "/" + fileName, e.getMessage(), e);
        }
    }

    /**
     * 尝试使用备用路径保存文件
     */
    private String tryBackupPath(String content, String fileName) {
        logger.warn("尝试使用备用路径保存文件...");

        String[] backupPaths = {
                "./",
                System.getProperty("user.home"),
                System.getProperty("java.io.tmpdir")
        };

        for (String backupDir : backupPaths) {
            try {
                Path backupPath = Paths.get(backupDir, fileName);

                try (FileWriter writer = new FileWriter(backupPath.toFile())) {
                    writer.write(content);
                    writer.flush();
                }

                String absolutePath = backupPath.toAbsolutePath().toString();
                logger.info("文档已保存到备用路径: {}", absolutePath);
                return absolutePath;

            } catch (IOException e) {
                logger.debug("备用路径 {} 保存失败: {}", backupDir, e.getMessage());
            }
        }

        logger.error("所有备用路径都保存失败");
        return null;
    }

    /**
     * 检查输出路径权限
     */
    public boolean checkOutputPathPermission() {
        try {
            Path outputDir = Paths.get(outputPath);

            // 如果目录不存在，尝试创建
            if (!Files.exists(outputDir)) {
                Files.createDirectories(outputDir);
            }

            // 检查是否可写
            if (!Files.isWritable(outputDir)) {
                logger.warn("输出目录不可写: {}", outputDir.toAbsolutePath());
                return false;
            }

            // 尝试创建临时文件测试权限
            Path testFile = outputDir.resolve("test_permission.tmp");
            try {
                Files.write(testFile, "test".getBytes(StandardCharsets.UTF_8));
                Files.deleteIfExists(testFile);
                return true;
            } catch (IOException e) {
                logger.warn("输出目录权限测试失败: {}", e.getMessage());
                return false;
            }

        } catch (Exception e) {
            logger.error("检查输出路径权限时发生错误: {}", e.getMessage(), e);
            return false;
        }
    }
}